@instructure/ui-modal 10.0.1-snapshot-11 → 10.0.1-snapshot-13

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
@@ -3,7 +3,7 @@
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
- ## [10.0.1-snapshot-11](https://github.com/instructure/instructure-ui/compare/v10.0.0...v10.0.1-snapshot-11) (2024-08-14)
6
+ ## [10.0.1-snapshot-13](https://github.com/instructure/instructure-ui/compare/v10.0.0...v10.0.1-snapshot-13) (2024-08-21)
7
7
 
8
8
  **Note:** Version bump only for package @instructure/ui-modal
9
9
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instructure/ui-modal",
3
- "version": "10.0.1-snapshot-11",
3
+ "version": "10.0.1-snapshot-13",
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.24.5",
27
- "@instructure/console": "10.0.1-snapshot-11",
28
- "@instructure/emotion": "10.0.1-snapshot-11",
29
- "@instructure/shared-types": "10.0.1-snapshot-11",
30
- "@instructure/ui-buttons": "10.0.1-snapshot-11",
31
- "@instructure/ui-dialog": "10.0.1-snapshot-11",
32
- "@instructure/ui-dom-utils": "10.0.1-snapshot-11",
33
- "@instructure/ui-motion": "10.0.1-snapshot-11",
34
- "@instructure/ui-overlays": "10.0.1-snapshot-11",
35
- "@instructure/ui-portal": "10.0.1-snapshot-11",
36
- "@instructure/ui-prop-types": "10.0.1-snapshot-11",
37
- "@instructure/ui-react-utils": "10.0.1-snapshot-11",
38
- "@instructure/ui-testable": "10.0.1-snapshot-11",
39
- "@instructure/ui-utils": "10.0.1-snapshot-11",
40
- "@instructure/ui-view": "10.0.1-snapshot-11",
27
+ "@instructure/console": "10.0.1-snapshot-13",
28
+ "@instructure/emotion": "10.0.1-snapshot-13",
29
+ "@instructure/shared-types": "10.0.1-snapshot-13",
30
+ "@instructure/ui-buttons": "10.0.1-snapshot-13",
31
+ "@instructure/ui-dialog": "10.0.1-snapshot-13",
32
+ "@instructure/ui-dom-utils": "10.0.1-snapshot-13",
33
+ "@instructure/ui-motion": "10.0.1-snapshot-13",
34
+ "@instructure/ui-overlays": "10.0.1-snapshot-13",
35
+ "@instructure/ui-portal": "10.0.1-snapshot-13",
36
+ "@instructure/ui-prop-types": "10.0.1-snapshot-13",
37
+ "@instructure/ui-react-utils": "10.0.1-snapshot-13",
38
+ "@instructure/ui-testable": "10.0.1-snapshot-13",
39
+ "@instructure/ui-utils": "10.0.1-snapshot-13",
40
+ "@instructure/ui-view": "10.0.1-snapshot-13",
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.0.1-snapshot-11",
48
- "@instructure/ui-color-utils": "10.0.1-snapshot-11",
49
- "@instructure/ui-position": "10.0.1-snapshot-11",
50
- "@instructure/ui-themes": "10.0.1-snapshot-11",
47
+ "@instructure/ui-babel-preset": "10.0.1-snapshot-13",
48
+ "@instructure/ui-color-utils": "10.0.1-snapshot-13",
49
+ "@instructure/ui-position": "10.0.1-snapshot-13",
50
+ "@instructure/ui-themes": "10.0.1-snapshot-13",
51
51
  "@testing-library/jest-dom": "^6.4.6",
52
52
  "@testing-library/react": "^15.0.7",
53
53
  "@testing-library/user-event": "^14.5.2",
@@ -8,79 +8,154 @@ overlays the application content and applies a mask to it.
8
8
  The default `padding` of the Modal content is `medium` but can be overridden
9
9
  by using the `padding` prop on the `<Modal.Body/>` if the use case requires it.
10
10
 
11
- ```js
12
- ---
13
- type: example
14
- ---
15
- const fpo = lorem.paragraphs(5)
11
+ - ```js
12
+ const fpo = lorem.paragraphs(5)
16
13
 
17
- class Example extends React.Component {
18
- constructor (props) {
19
- super(props)
14
+ class Example extends React.Component {
15
+ constructor(props) {
16
+ super(props)
20
17
 
21
- this.state = {
22
- open: false
18
+ this.state = {
19
+ open: false
20
+ }
23
21
  }
24
- }
25
22
 
26
- handleButtonClick = () => {
27
- this.setState(function (state) {
28
- return { open: !state.open }
29
- })
30
- };
23
+ handleButtonClick = () => {
24
+ this.setState(function (state) {
25
+ return { open: !state.open }
26
+ })
27
+ }
31
28
 
32
- handleFormSubmit = e => {
33
- e.preventDefault()
34
- console.log('form submitted')
35
- this.setState(state => ({ open: false }))
36
- }
29
+ handleFormSubmit = (e) => {
30
+ e.preventDefault()
31
+ console.log('form submitted')
32
+ this.setState((state) => ({ open: false }))
33
+ }
37
34
 
38
- renderCloseButton () {
39
- return (
40
- <CloseButton
41
- placement="end"
42
- offset="small"
43
- onClick={this.handleButtonClick}
44
- screenReaderLabel="Close"
45
- />
46
- )
35
+ renderCloseButton() {
36
+ return (
37
+ <CloseButton
38
+ placement="end"
39
+ offset="small"
40
+ onClick={this.handleButtonClick}
41
+ screenReaderLabel="Close"
42
+ />
43
+ )
44
+ }
45
+
46
+ render() {
47
+ return (
48
+ <div style={{ padding: '0 0 11rem 0', margin: '0 auto' }}>
49
+ <Button onClick={this.handleButtonClick}>
50
+ {this.state.open ? 'Close' : 'Open'} the Modal
51
+ </Button>
52
+ <Modal
53
+ as="form"
54
+ open={this.state.open}
55
+ onDismiss={() => {
56
+ this.setState({ open: false })
57
+ }}
58
+ onSubmit={this.handleFormSubmit}
59
+ size="auto"
60
+ label="Modal Dialog: Hello World"
61
+ shouldCloseOnDocumentClick
62
+ >
63
+ <Modal.Header>
64
+ {this.renderCloseButton()}
65
+ <Heading>Hello World</Heading>
66
+ </Modal.Header>
67
+ <Modal.Body>
68
+ <TextInput
69
+ renderLabel="Example"
70
+ placeholder="if you hit enter here, it should submit the form"
71
+ />
72
+ <Text lineHeight="double">{fpo}</Text>
73
+ </Modal.Body>
74
+ <Modal.Footer>
75
+ <Button onClick={this.handleButtonClick} margin="0 x-small 0 0">
76
+ Close
77
+ </Button>
78
+ <Button color="primary" type="submit">
79
+ Submit
80
+ </Button>
81
+ </Modal.Footer>
82
+ </Modal>
83
+ </div>
84
+ )
85
+ }
47
86
  }
48
87
 
49
- render () {
88
+ render(<Example />)
89
+ ```
90
+
91
+ - ```js
92
+ const fpo = lorem.paragraphs(5)
93
+ const Example = () => {
94
+ const [open, setOpen] = useState(false)
95
+
96
+ const handleButtonClick = () => {
97
+ setOpen((state) => !state)
98
+ }
99
+
100
+ const handleFormSubmit = (e) => {
101
+ e.preventDefault()
102
+ console.log('form submitted')
103
+ setOpen(false)
104
+ }
105
+
106
+ const renderCloseButton = () => {
107
+ return (
108
+ <CloseButton
109
+ placement="end"
110
+ offset="small"
111
+ onClick={handleButtonClick}
112
+ screenReaderLabel="Close"
113
+ />
114
+ )
115
+ }
116
+
50
117
  return (
51
118
  <div style={{ padding: '0 0 11rem 0', margin: '0 auto' }}>
52
- <Button onClick={this.handleButtonClick}>
53
- {this.state.open ? 'Close' : 'Open'} the Modal
119
+ <Button onClick={handleButtonClick}>
120
+ {open ? 'Close' : 'Open'} the Modal
54
121
  </Button>
55
122
  <Modal
56
123
  as="form"
57
- open={this.state.open}
58
- onDismiss={() => { this.setState({ open: false }) }}
59
- onSubmit={this.handleFormSubmit}
124
+ open={open}
125
+ onDismiss={() => {
126
+ setOpen(false)
127
+ }}
128
+ onSubmit={handleFormSubmit}
60
129
  size="auto"
61
130
  label="Modal Dialog: Hello World"
62
131
  shouldCloseOnDocumentClick
63
132
  >
64
133
  <Modal.Header>
65
- {this.renderCloseButton()}
134
+ {renderCloseButton()}
66
135
  <Heading>Hello World</Heading>
67
136
  </Modal.Header>
68
137
  <Modal.Body>
69
- <TextInput renderLabel="Example" placeholder="if you hit enter here, it should submit the form" />
138
+ <TextInput
139
+ renderLabel="Example"
140
+ placeholder="if you hit enter here, it should submit the form"
141
+ />
70
142
  <Text lineHeight="double">{fpo}</Text>
71
143
  </Modal.Body>
72
144
  <Modal.Footer>
73
- <Button onClick={this.handleButtonClick} margin="0 x-small 0 0">Close</Button>
74
- <Button color="primary" type="submit">Submit</Button>
145
+ <Button onClick={handleButtonClick} margin="0 x-small 0 0">
146
+ Close
147
+ </Button>
148
+ <Button color="primary" type="submit">
149
+ Submit
150
+ </Button>
75
151
  </Modal.Footer>
76
152
  </Modal>
77
153
  </div>
78
154
  )
79
155
  }
80
- }
81
156
 
82
- render(<Example />)
83
- ```
157
+ render(<Example />)
158
+ ```
84
159
 
85
160
  ### Constraining Modal to a parent element
86
161
 
@@ -88,47 +163,171 @@ By default, Modals are positioned relative to the document body.
88
163
 
89
164
  Setting the `constrain` property to `parent` will constrain the Modal within the element it is mounted from (specified via the `mountNode` property). Note: in these cases, the `mountNode` element should have an explicit `height` set: Because Modal is absolutely positioned, it has no height of its own.
90
165
 
91
- ```js
92
- ---
93
- type: example
94
- ---
95
- const fpo = lorem.paragraphs(1)
96
- class Example extends React.Component {
97
- constructor (props) {
98
- super(props)
166
+ - ```js
167
+ const fpo = lorem.paragraphs(1)
168
+ class Example extends React.Component {
169
+ constructor(props) {
170
+ super(props)
99
171
 
100
- this.state = {
101
- open: false,
102
- size: 'auto'
172
+ this.state = {
173
+ open: false,
174
+ size: 'auto'
175
+ }
176
+ }
177
+
178
+ handleButtonClick = () => {
179
+ this.setState(function (state) {
180
+ return { open: !state.open }
181
+ })
182
+ }
183
+
184
+ renderCloseButton() {
185
+ return (
186
+ <CloseButton
187
+ placement="end"
188
+ offset="small"
189
+ onClick={this.handleButtonClick}
190
+ screenReaderLabel="Close"
191
+ />
192
+ )
103
193
  }
104
- }
105
194
 
106
- handleButtonClick = () => {
107
- this.setState(function (state) {
108
- return { open: !state.open }
109
- })
195
+ render() {
196
+ return (
197
+ <div>
198
+ <Button onClick={this.handleButtonClick}>
199
+ {this.state.open ? 'Close' : 'Open'} the Modal
200
+ </Button>
201
+ <Modal
202
+ open={this.state.open}
203
+ onDismiss={() => {
204
+ this.setState({ open: false })
205
+ }}
206
+ size="fullscreen"
207
+ label="Modal Dialog: Hello World"
208
+ shouldCloseOnDocumentClick
209
+ mountNode={() => document.getElementById('constrainExample')}
210
+ constrain="parent"
211
+ >
212
+ <Modal.Header>
213
+ {this.renderCloseButton()}
214
+ <Heading>This Modal is constrained to a parent</Heading>
215
+ </Modal.Header>
216
+ <Modal.Body>
217
+ <View as="p" margin="none none small">
218
+ <Text>{fpo}</Text>
219
+ </View>
220
+ <ModalAutoCompleteExample renderLabel="Choose a state" />
221
+ </Modal.Body>
222
+ <Modal.Footer>
223
+ <Button onClick={this.handleButtonClick} margin="0 x-small 0 0">
224
+ Close
225
+ </Button>
226
+ <Button
227
+ onClick={this.handleButtonClick}
228
+ color="primary"
229
+ type="submit"
230
+ >
231
+ Submit
232
+ </Button>
233
+ </Modal.Footer>
234
+ </Modal>
235
+ <View
236
+ background="primary-inverse"
237
+ margin="medium auto none"
238
+ display="block"
239
+ width="25rem"
240
+ height="25rem"
241
+ borderWidth="large"
242
+ id="constrainExample"
243
+ ></View>
244
+ </div>
245
+ )
246
+ }
110
247
  }
111
248
 
112
- renderCloseButton () {
113
- return (
114
- <CloseButton
115
- placement="end"
116
- offset="small"
117
- onClick={this.handleButtonClick}
118
- screenReaderLabel="Close"
119
- />
120
- )
249
+ class ModalAutoCompleteExample extends React.Component {
250
+ state = {
251
+ isShowingOptions: false
252
+ }
253
+
254
+ handleShowOptions = () => {
255
+ this.setState({ isShowingOptions: true })
256
+ }
257
+ handleHideOptions = () => {
258
+ this.setState({ isShowingOptions: false })
259
+ }
260
+ render() {
261
+ const options = [
262
+ 'Alabama',
263
+ 'Alaska',
264
+ 'American Samoa',
265
+ 'Arizona',
266
+ 'Arkansas',
267
+ 'California',
268
+ 'Colorado',
269
+ 'Connecticut',
270
+ 'Delaware',
271
+ 'District Of Columbia',
272
+ 'Federated States Of Micronesia',
273
+ 'Florida',
274
+ 'Georgia',
275
+ 'Guam',
276
+ 'Hawaii',
277
+ 'Idaho',
278
+ 'Illinois'
279
+ ]
280
+
281
+ return (
282
+ <Select
283
+ {...this.props}
284
+ isShowingOptions={this.state.isShowingOptions}
285
+ onRequestShowOptions={this.handleShowOptions}
286
+ onRequestHideOptions={this.handleHideOptions}
287
+ >
288
+ {options.map((label, index) => (
289
+ <Select.Option key={label} id={'' + index}>
290
+ {label}
291
+ </Select.Option>
292
+ ))}
293
+ </Select>
294
+ )
295
+ }
121
296
  }
122
297
 
123
- render () {
298
+ render(<Example />)
299
+ ```
300
+
301
+ - ```js
302
+ const fpo = lorem.paragraphs(1)
303
+ const Example = () => {
304
+ const [open, setOpen] = useState(false)
305
+ const [size, setSize] = useState('auto')
306
+
307
+ const handleButtonClick = () => {
308
+ setOpen((state) => !state)
309
+ }
310
+
311
+ const renderCloseButton = () => {
312
+ return (
313
+ <CloseButton
314
+ placement="end"
315
+ offset="small"
316
+ onClick={handleButtonClick}
317
+ screenReaderLabel="Close"
318
+ />
319
+ )
320
+ }
124
321
  return (
125
322
  <div>
126
- <Button onClick={this.handleButtonClick}>
127
- {this.state.open ? 'Close' : 'Open'} the Modal
323
+ <Button onClick={handleButtonClick}>
324
+ {open ? 'Close' : 'Open'} the Modal
128
325
  </Button>
129
326
  <Modal
130
- open={this.state.open}
131
- onDismiss={() => { this.setState({ open: false }) }}
327
+ open={open}
328
+ onDismiss={() => {
329
+ setOpen(false)
330
+ }}
132
331
  size="fullscreen"
133
332
  label="Modal Dialog: Hello World"
134
333
  shouldCloseOnDocumentClick
@@ -136,16 +335,22 @@ class Example extends React.Component {
136
335
  constrain="parent"
137
336
  >
138
337
  <Modal.Header>
139
- {this.renderCloseButton()}
338
+ {renderCloseButton()}
140
339
  <Heading>This Modal is constrained to a parent</Heading>
141
340
  </Modal.Header>
142
341
  <Modal.Body>
143
- <View as="p" margin="none none small"><Text>{fpo}</Text></View>
342
+ <View as="p" margin="none none small">
343
+ <Text>{fpo}</Text>
344
+ </View>
144
345
  <ModalAutoCompleteExample renderLabel="Choose a state" />
145
346
  </Modal.Body>
146
347
  <Modal.Footer>
147
- <Button onClick={this.handleButtonClick} margin="0 x-small 0 0">Close</Button>
148
- <Button onClick={this.handleButtonClick} color="primary" type="submit">Submit</Button>
348
+ <Button onClick={handleButtonClick} margin="0 x-small 0 0">
349
+ Close
350
+ </Button>
351
+ <Button onClick={handleButtonClick} color="primary" type="submit">
352
+ Submit
353
+ </Button>
149
354
  </Modal.Footer>
150
355
  </Modal>
151
356
  <View
@@ -155,35 +360,48 @@ class Example extends React.Component {
155
360
  width="25rem"
156
361
  height="25rem"
157
362
  borderWidth="large"
158
- id="constrainExample">
159
- </View>
363
+ id="constrainExample"
364
+ ></View>
160
365
  </div>
161
366
  )
162
367
  }
163
- }
164
368
 
165
- class ModalAutoCompleteExample extends React.Component {
166
- state = {
167
- isShowingOptions: false
168
- }
369
+ const ModalAutoCompleteExample = (props) => {
370
+ const [isShowingOptions, setIsShowingOptions] = useState(false)
371
+
372
+ const handleShowOptions = () => {
373
+ setIsShowingOptions(true)
374
+ }
375
+ const handleHideOptions = () => {
376
+ setIsShowingOptions(false)
377
+ }
169
378
 
170
- handleShowOptions = () => {
171
- this.setState({ isShowingOptions: true })
172
- }
173
- handleHideOptions = () => {
174
- this.setState({ isShowingOptions: false })
175
- }
176
- render () {
177
379
  const options = [
178
- 'Alabama', 'Alaska', 'American Samoa', 'Arizona',
179
- 'Arkansas', 'California', 'Colorado', 'Connecticut',
180
- 'Delaware', 'District Of Columbia',
181
- 'Federated States Of Micronesia', 'Florida', 'Georgia',
182
- 'Guam', 'Hawaii', 'Idaho', 'Illinois'
380
+ 'Alabama',
381
+ 'Alaska',
382
+ 'American Samoa',
383
+ 'Arizona',
384
+ 'Arkansas',
385
+ 'California',
386
+ 'Colorado',
387
+ 'Connecticut',
388
+ 'Delaware',
389
+ 'District Of Columbia',
390
+ 'Federated States Of Micronesia',
391
+ 'Florida',
392
+ 'Georgia',
393
+ 'Guam',
394
+ 'Hawaii',
395
+ 'Idaho',
396
+ 'Illinois'
183
397
  ]
184
-
185
398
  return (
186
- <Select {...this.props} isShowingOptions={this.state.isShowingOptions} onRequestShowOptions={this.handleShowOptions} onRequestHideOptions={this.handleHideOptions}>
399
+ <Select
400
+ {...props}
401
+ isShowingOptions={isShowingOptions}
402
+ onRequestShowOptions={handleShowOptions}
403
+ onRequestHideOptions={handleHideOptions}
404
+ >
187
405
  {options.map((label, index) => (
188
406
  <Select.Option key={label} id={'' + index}>
189
407
  {label}
@@ -192,10 +410,9 @@ class ModalAutoCompleteExample extends React.Component {
192
410
  </Select>
193
411
  )
194
412
  }
195
- }
196
413
 
197
- render(<Example />)
198
- ```
414
+ render(<Example />)
415
+ ```
199
416
 
200
417
  ### Media (images, etc.) inside Modals
201
418
 
@@ -203,34 +420,100 @@ render(<Example />)
203
420
 
204
421
  **If you are displaying small, relatively uniform images or videos inside Modal, the default settings should work well.** Modal.Body will expand to the height of the media you're displaying. If there is overflow, scrollbars will be available.
205
422
 
206
- ```js
207
- ---
208
- type: example
209
- ---
210
- class Example extends React.Component {
211
- constructor (props) {
212
- super(props)
423
+ - ```js
424
+ class Example extends React.Component {
425
+ constructor(props) {
426
+ super(props)
213
427
 
214
- this.state = {
215
- open: false
428
+ this.state = {
429
+ open: false
430
+ }
431
+ }
432
+
433
+ handleButtonClick = () => {
434
+ this.setState(function (state) {
435
+ return { open: !state.open }
436
+ })
216
437
  }
217
- }
218
438
 
219
- handleButtonClick = () => {
220
- this.setState(function (state) {
221
- return { open: !state.open }
222
- })
439
+ render() {
440
+ return (
441
+ <div>
442
+ <Button onClick={this.handleButtonClick}>
443
+ {this.state.open ? 'Close' : 'Open'} the Modal
444
+ </Button>
445
+ <Modal
446
+ open={this.state.open}
447
+ onDismiss={() => {
448
+ this.setState({ open: false })
449
+ }}
450
+ size="auto"
451
+ label="Modal Dialog: Hello Media"
452
+ shouldCloseOnDocumentClick
453
+ variant="inverse"
454
+ >
455
+ <Modal.Header>
456
+ <Flex>
457
+ <Flex.Item shouldGrow shouldShrink>
458
+ <Heading level="h2">
459
+ <TruncateText>A small image</TruncateText>
460
+ </Heading>
461
+ </Flex.Item>
462
+ <Flex.Item>
463
+ <CloseButton
464
+ color="primary-inverse"
465
+ placement="end"
466
+ offset="small"
467
+ onClick={this.handleButtonClick}
468
+ screenReaderLabel="Close"
469
+ />
470
+ </Flex.Item>
471
+ </Flex>
472
+ </Modal.Header>
473
+ <Modal.Body padding="none">
474
+ <Img
475
+ src={placeholderImage(500, 250)}
476
+ display="block"
477
+ margin="0 auto"
478
+ />
479
+ </Modal.Body>
480
+ <Modal.Footer>
481
+ <Button
482
+ onClick={this.handleButtonClick}
483
+ withBackground={false}
484
+ color="primary-inverse"
485
+ type="submit"
486
+ >
487
+ Submit
488
+ </Button>
489
+ </Modal.Footer>
490
+ </Modal>
491
+ </div>
492
+ )
493
+ }
223
494
  }
224
495
 
225
- render () {
496
+ render(<Example />)
497
+ ```
498
+
499
+ - ```js
500
+ const Example = () => {
501
+ const [open, setOpen] = useState(false)
502
+
503
+ const handleButtonClick = () => {
504
+ setOpen((state) => !state)
505
+ }
506
+
226
507
  return (
227
508
  <div>
228
- <Button onClick={this.handleButtonClick}>
229
- {this.state.open ? 'Close' : 'Open'} the Modal
509
+ <Button onClick={handleButtonClick}>
510
+ {open ? 'Close' : 'Open'} the Modal
230
511
  </Button>
231
512
  <Modal
232
- open={this.state.open}
233
- onDismiss={() => { this.setState({ open: false }) }}
513
+ open={open}
514
+ onDismiss={() => {
515
+ setOpen(false)
516
+ }}
234
517
  size="auto"
235
518
  label="Modal Dialog: Hello Media"
236
519
  shouldCloseOnDocumentClick
@@ -239,14 +522,16 @@ class Example extends React.Component {
239
522
  <Modal.Header>
240
523
  <Flex>
241
524
  <Flex.Item shouldGrow shouldShrink>
242
- <Heading level="h2"><TruncateText>A small image</TruncateText></Heading>
525
+ <Heading level="h2">
526
+ <TruncateText>A small image</TruncateText>
527
+ </Heading>
243
528
  </Flex.Item>
244
529
  <Flex.Item>
245
530
  <CloseButton
246
531
  color="primary-inverse"
247
532
  placement="end"
248
533
  offset="small"
249
- onClick={this.handleButtonClick}
534
+ onClick={handleButtonClick}
250
535
  screenReaderLabel="Close"
251
536
  />
252
537
  </Flex.Item>
@@ -260,60 +545,205 @@ class Example extends React.Component {
260
545
  />
261
546
  </Modal.Body>
262
547
  <Modal.Footer>
263
- <Button onClick={this.handleButtonClick} withBackground={false} color="primary-inverse" type="submit">Submit</Button>
548
+ <Button
549
+ onClick={handleButtonClick}
550
+ withBackground={false}
551
+ color="primary-inverse"
552
+ type="submit"
553
+ >
554
+ Submit
555
+ </Button>
264
556
  </Modal.Footer>
265
557
  </Modal>
266
558
  </div>
267
559
  )
268
560
  }
269
- }
270
561
 
271
- render(<Example />)
272
- ```
562
+ render(<Example />)
563
+ ```
273
564
 
274
565
  **When you have to display large media inside the Modal (or have no control over the size of the media)**, set `overflow` to `fit`. Doing so makes Modal.Body fill 100% of the available width and height, enabling you to
275
566
  use the [Img](#Img) component's `constrain` property to fit the image inside Modal.Body.
276
567
 
277
568
  > `<Img />` uses CSS's [`object-fit`](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) for its constrain property. If you're not using `<Img />`, add an `object-fit` rule to your media, and it will work with `overflow="fit"`.
278
569
 
279
- ```js
280
- ---
281
- type: example
282
- ---
283
- class Example extends React.Component {
284
- constructor (props) {
285
- super(props)
570
+ - ```js
571
+ class Example extends React.Component {
572
+ constructor(props) {
573
+ super(props)
286
574
 
287
- this.state = {
288
- open: false,
289
- imageFit: 'cover',
290
- modalSize: 'fullscreen'
575
+ this.state = {
576
+ open: false,
577
+ imageFit: 'cover',
578
+ modalSize: 'fullscreen'
579
+ }
291
580
  }
292
- }
293
581
 
294
- handleButtonClick = () => {
295
- this.setState(function (state) {
296
- return { open: !state.open }
297
- })
298
- }
582
+ handleButtonClick = () => {
583
+ this.setState(function (state) {
584
+ return { open: !state.open }
585
+ })
586
+ }
299
587
 
300
- handleImageFitChange = (event, value) => {
301
- this.setState({ imageFit: value })
302
- }
588
+ handleImageFitChange = (event, value) => {
589
+ this.setState({ imageFit: value })
590
+ }
303
591
 
304
- handleModalSizeChange = (event, value) => {
305
- this.setState({ modalSize: value })
592
+ handleModalSizeChange = (event, value) => {
593
+ this.setState({ modalSize: value })
594
+ }
595
+
596
+ render() {
597
+ return (
598
+ <div>
599
+ <FormFieldGroup
600
+ description={
601
+ <Heading level="h3" as="h3">
602
+ Media Modal
603
+ </Heading>
604
+ }
605
+ rowSpacing="medium"
606
+ >
607
+ <RadioInputGroup
608
+ onChange={this.handleImageFitChange}
609
+ name="imageFit"
610
+ defaultValue="cover"
611
+ description="Img component's `constrain` prop"
612
+ variant="toggle"
613
+ >
614
+ <RadioInput label="Cover" value="cover" />
615
+ <RadioInput label="Contain" value="contain" />
616
+ </RadioInputGroup>
617
+ <RadioInputGroup
618
+ onChange={this.handleModalSizeChange}
619
+ name="modalSize"
620
+ defaultValue="fullscreen"
621
+ description="Modal size"
622
+ variant="toggle"
623
+ >
624
+ <RadioInput label="fullscreen" value="fullscreen" />
625
+ <RadioInput label="small" value="small" />
626
+ <RadioInput label="medium" value="medium" />
627
+ <RadioInput label="large" value="large" />
628
+ <RadioInput label="auto" value="auto" />
629
+ </RadioInputGroup>
630
+ </FormFieldGroup>
631
+ <Button onClick={this.handleButtonClick} margin="medium 0 0">
632
+ {this.state.open ? 'Close' : 'Open'} the Modal
633
+ </Button>
634
+ <Modal
635
+ open={this.state.open}
636
+ onDismiss={() => {
637
+ this.setState({ open: false })
638
+ }}
639
+ size={this.state.modalSize}
640
+ label="Modal Dialog: Hello Media"
641
+ shouldCloseOnDocumentClick
642
+ variant="inverse"
643
+ overflow="fit"
644
+ >
645
+ <Modal.Header>
646
+ <Flex>
647
+ <Flex.Item shouldGrow shouldShrink>
648
+ <Flex alignItems="center">
649
+ <Flex.Item margin="0 x-small 0 0">
650
+ <SVGIcon
651
+ src={iconExample}
652
+ size="small"
653
+ title="Icon Example"
654
+ />
655
+ </Flex.Item>
656
+ <Flex.Item shouldGrow shouldShrink>
657
+ <Heading level="h2">
658
+ <TruncateText>This Modal Contains Media</TruncateText>
659
+ </Heading>
660
+ </Flex.Item>
661
+ </Flex>
662
+ </Flex.Item>
663
+ <Flex.Item>
664
+ <IconButton
665
+ color="primary-inverse"
666
+ withBackground={false}
667
+ withBorder={false}
668
+ renderIcon={IconPrinterSolid}
669
+ screenReaderLabel="Print This Image"
670
+ margin="0 x-small 0 0"
671
+ />
672
+ <IconButton
673
+ color="primary-inverse"
674
+ withBackground={false}
675
+ withBorder={false}
676
+ renderIcon={IconDownloadSolid}
677
+ screenReaderLabel="Download This Image"
678
+ margin="0 x-small 0 0"
679
+ />
680
+ <IconButton
681
+ color="primary-inverse"
682
+ withBackground={false}
683
+ withBorder={false}
684
+ renderIcon={IconXSolid}
685
+ screenReaderLabel="Close"
686
+ onClick={this.handleButtonClick}
687
+ />
688
+ </Flex.Item>
689
+ </Flex>
690
+ </Modal.Header>
691
+ <Modal.Body padding="none">
692
+ <Img
693
+ src={avatarSquare}
694
+ constrain={this.state.imageFit}
695
+ display="block"
696
+ />
697
+ </Modal.Body>
698
+ <Modal.Footer>
699
+ <Button
700
+ onClick={this.handleButtonClick}
701
+ withBackground={false}
702
+ color="primary-inverse"
703
+ type="submit"
704
+ >
705
+ Close
706
+ </Button>
707
+ </Modal.Footer>
708
+ </Modal>
709
+ </div>
710
+ )
711
+ }
306
712
  }
307
713
 
308
- render () {
714
+ render(<Example />)
715
+ ```
716
+
717
+ - ```js
718
+ const Example = () => {
719
+ const [open, setOpen] = useState(false)
720
+ const [imageFit, setImageFit] = useState('cover')
721
+ const [modalSize, setModalSize] = useState('fullscreen')
722
+
723
+ const handleButtonClick = () => {
724
+ setOpen((state) => !state)
725
+ }
726
+
727
+ const handleImageFitChange = (event, value) => {
728
+ setImageFit(value)
729
+ }
730
+
731
+ const handleModalSizeChange = (event, value) => {
732
+ setModalSize(value)
733
+ }
734
+
309
735
  return (
310
736
  <div>
311
737
  <FormFieldGroup
312
- description={<Heading level="h3" as="h3">Media Modal</Heading>}
738
+ description={
739
+ <Heading level="h3" as="h3">
740
+ Media Modal
741
+ </Heading>
742
+ }
313
743
  rowSpacing="medium"
314
744
  >
315
745
  <RadioInputGroup
316
- onChange={this.handleImageFitChange}
746
+ onChange={handleImageFitChange}
317
747
  name="imageFit"
318
748
  defaultValue="cover"
319
749
  description="Img component's `constrain` prop"
@@ -323,7 +753,7 @@ class Example extends React.Component {
323
753
  <RadioInput label="Contain" value="contain" />
324
754
  </RadioInputGroup>
325
755
  <RadioInputGroup
326
- onChange={this.handleModalSizeChange}
756
+ onChange={handleModalSizeChange}
327
757
  name="modalSize"
328
758
  defaultValue="fullscreen"
329
759
  description="Modal size"
@@ -336,13 +766,15 @@ class Example extends React.Component {
336
766
  <RadioInput label="auto" value="auto" />
337
767
  </RadioInputGroup>
338
768
  </FormFieldGroup>
339
- <Button onClick={this.handleButtonClick} margin="medium 0 0">
340
- {this.state.open ? 'Close' : 'Open'} the Modal
769
+ <Button onClick={handleButtonClick} margin="medium 0 0">
770
+ {open ? 'Close' : 'Open'} the Modal
341
771
  </Button>
342
772
  <Modal
343
- open={this.state.open}
344
- onDismiss={() => { this.setState({ open: false }) }}
345
- size={this.state.modalSize}
773
+ open={open}
774
+ onDismiss={() => {
775
+ setOpen(false)
776
+ }}
777
+ size={modalSize}
346
778
  label="Modal Dialog: Hello Media"
347
779
  shouldCloseOnDocumentClick
348
780
  variant="inverse"
@@ -353,10 +785,16 @@ class Example extends React.Component {
353
785
  <Flex.Item shouldGrow shouldShrink>
354
786
  <Flex alignItems="center">
355
787
  <Flex.Item margin="0 x-small 0 0">
356
- <SVGIcon src={iconExample} size="small" title="Icon Example" />
788
+ <SVGIcon
789
+ src={iconExample}
790
+ size="small"
791
+ title="Icon Example"
792
+ />
357
793
  </Flex.Item>
358
794
  <Flex.Item shouldGrow shouldShrink>
359
- <Heading level="h2"><TruncateText>This Modal Contains Media</TruncateText></Heading>
795
+ <Heading level="h2">
796
+ <TruncateText>This Modal Contains Media</TruncateText>
797
+ </Heading>
360
798
  </Flex.Item>
361
799
  </Flex>
362
800
  </Flex.Item>
@@ -383,91 +821,188 @@ class Example extends React.Component {
383
821
  withBorder={false}
384
822
  renderIcon={IconXSolid}
385
823
  screenReaderLabel="Close"
386
- onClick={this.handleButtonClick}
824
+ onClick={handleButtonClick}
387
825
  />
388
826
  </Flex.Item>
389
827
  </Flex>
390
828
  </Modal.Header>
391
829
  <Modal.Body padding="none">
392
- <Img
393
- src={avatarSquare}
394
- constrain={this.state.imageFit}
395
- display="block"
396
- />
830
+ <Img src={avatarSquare} constrain={imageFit} display="block" />
397
831
  </Modal.Body>
398
832
  <Modal.Footer>
399
- <Button onClick={this.handleButtonClick} withBackground={false} color="primary-inverse" type="submit">Close</Button>
833
+ <Button
834
+ onClick={handleButtonClick}
835
+ withBackground={false}
836
+ color="primary-inverse"
837
+ type="submit"
838
+ >
839
+ Close
840
+ </Button>
400
841
  </Modal.Footer>
401
842
  </Modal>
402
843
  </div>
403
844
  )
404
845
  }
405
- }
406
846
 
407
- render(<Example />)
408
- ```
847
+ render(<Example />)
848
+ ```
409
849
 
410
850
  ### Small viewports
411
851
 
412
852
  On smaller viewports (like mobile devices or scaled-up UI), we don't want to lose space because of padding and margins. In order to achieve that, use `size="fullscreen"` on the Modal and set the `spacing` property of Modal.Header to `compact`.
413
853
 
414
- ```js
415
- ---
416
- type: example
417
- ---
418
- const fpo = lorem.paragraphs(1)
419
- class Example extends React.Component {
420
- constructor (props) {
421
- super(props)
854
+ - ```js
855
+ const fpo = lorem.paragraphs(1)
856
+ class Example extends React.Component {
857
+ constructor(props) {
858
+ super(props)
422
859
 
423
- this.state = {
424
- open: false,
425
- smallViewport: true
860
+ this.state = {
861
+ open: false,
862
+ smallViewport: true
863
+ }
426
864
  }
427
- }
428
865
 
429
- toggleOpen = () => {
430
- this.setState(function (state) {
431
- return { open: !state.open }
432
- })
433
- }
866
+ toggleOpen = () => {
867
+ this.setState(function (state) {
868
+ return { open: !state.open }
869
+ })
870
+ }
434
871
 
435
- toggleViewport = async () => {
436
- await this.setState(function (state) {
437
- return { smallViewport: !state.smallViewport }
438
- })
439
- }
872
+ toggleViewport = async () => {
873
+ await this.setState(function (state) {
874
+ return { smallViewport: !state.smallViewport }
875
+ })
876
+ }
440
877
 
441
- renderCloseButton () {
442
- return (
443
- <CloseButton
444
- placement="end"
445
- offset="small"
446
- onClick={this.toggleOpen}
447
- screenReaderLabel="Close"
448
- />
449
- )
878
+ renderCloseButton() {
879
+ return (
880
+ <CloseButton
881
+ placement="end"
882
+ offset="small"
883
+ onClick={this.toggleOpen}
884
+ screenReaderLabel="Close"
885
+ />
886
+ )
887
+ }
888
+
889
+ render() {
890
+ return (
891
+ <div>
892
+ <Button onClick={this.toggleOpen}>
893
+ {this.state.open ? 'Close' : 'Open'} the Modal
894
+ </Button>
895
+ <Button
896
+ onClick={this.toggleViewport}
897
+ margin="0 0 0 small"
898
+ id="toggleViewportButton"
899
+ >
900
+ Toggle viewport
901
+ </Button>
902
+ <Modal
903
+ open={this.state.open}
904
+ size={this.state.smallViewport ? 'fullscreen' : 'small'}
905
+ onDismiss={(event) => {
906
+ if (event.target.id !== 'toggleViewportButton') {
907
+ this.setState({ open: false })
908
+ }
909
+ }}
910
+ label="Modal Dialog: Hello World"
911
+ shouldCloseOnDocumentClick
912
+ mountNode={() => document.getElementById('viewportExample')}
913
+ constrain="parent"
914
+ >
915
+ <Modal.Header
916
+ spacing={this.state.smallViewport ? 'compact' : 'default'}
917
+ >
918
+ {this.renderCloseButton()}
919
+ {this.state.smallViewport ? (
920
+ <Heading
921
+ as="h2"
922
+ level="h3"
923
+ themeOverride={{ h3FontWeight: 400 }}
924
+ >
925
+ This Modal is optimized for small viewport
926
+ </Heading>
927
+ ) : (
928
+ <Heading as="h2">This is a default size Modal</Heading>
929
+ )}
930
+ </Modal.Header>
931
+ <Modal.Body>
932
+ <View as="p" margin="none none small">
933
+ <Text>{fpo}</Text>
934
+ </View>
935
+ </Modal.Body>
936
+ <Modal.Footer>
937
+ <Button onClick={this.toggleOpen} margin="0 x-small 0 0">
938
+ Close
939
+ </Button>
940
+ <Button onClick={this.toggleOpen} color="primary" type="submit">
941
+ Submit
942
+ </Button>
943
+ </Modal.Footer>
944
+ </Modal>
945
+
946
+ <View
947
+ background="primary-inverse"
948
+ margin="medium auto none"
949
+ display="block"
950
+ width={this.state.smallViewport ? '20rem' : '50rem'}
951
+ height="37.5rem"
952
+ borderWidth="large"
953
+ id="viewportExample"
954
+ ></View>
955
+ </div>
956
+ )
957
+ }
450
958
  }
451
959
 
452
- render () {
960
+ render(<Example />)
961
+ ```
962
+
963
+ - ```js
964
+ const fpo = lorem.paragraphs(1)
965
+ const Example = () => {
966
+ const [open, setOpen] = useState(false)
967
+ const [smallViewport, setSmallViewport] = useState(true)
968
+
969
+ const toggleOpen = () => {
970
+ setOpen((state) => !state)
971
+ }
972
+
973
+ const toggleViewport = () => {
974
+ setSmallViewport((state) => !state)
975
+ }
976
+
977
+ const renderCloseButton = () => {
978
+ return (
979
+ <CloseButton
980
+ placement="end"
981
+ offset="small"
982
+ onClick={toggleOpen}
983
+ screenReaderLabel="Close"
984
+ />
985
+ )
986
+ }
987
+
453
988
  return (
454
989
  <div>
455
- <Button onClick={this.toggleOpen}>
456
- {this.state.open ? 'Close' : 'Open'} the Modal
990
+ <Button onClick={toggleOpen}>
991
+ {open ? 'Close' : 'Open'} the Modal
457
992
  </Button>
458
993
  <Button
459
- onClick={this.toggleViewport}
460
- margin='0 0 0 small'
994
+ onClick={toggleViewport}
995
+ margin="0 0 0 small"
461
996
  id="toggleViewportButton"
462
997
  >
463
998
  Toggle viewport
464
999
  </Button>
465
1000
  <Modal
466
- open={this.state.open}
467
- size={this.state.smallViewport ? 'fullscreen' : 'small'}
1001
+ open={open}
1002
+ size={smallViewport ? 'fullscreen' : 'small'}
468
1003
  onDismiss={(event) => {
469
1004
  if (event.target.id !== 'toggleViewportButton') {
470
- this.setState({ open: false })
1005
+ setOpen(false)
471
1006
  }
472
1007
  }}
473
1008
  label="Modal Dialog: Hello World"
@@ -475,19 +1010,28 @@ class Example extends React.Component {
475
1010
  mountNode={() => document.getElementById('viewportExample')}
476
1011
  constrain="parent"
477
1012
  >
478
- <Modal.Header spacing={this.state.smallViewport ? 'compact' : 'default'}>
479
- {this.renderCloseButton()}
480
- {this.state.smallViewport
481
- ? <Heading as="h2" level="h3" themeOverride={{ h3FontWeight: 400 }}>This Modal is optimized for small viewport</Heading>
482
- : <Heading as="h2">This is a default size Modal</Heading>
483
- }
1013
+ <Modal.Header spacing={smallViewport ? 'compact' : 'default'}>
1014
+ {renderCloseButton()}
1015
+ {smallViewport ? (
1016
+ <Heading as="h2" level="h3" themeOverride={{ h3FontWeight: 400 }}>
1017
+ This Modal is optimized for small viewport
1018
+ </Heading>
1019
+ ) : (
1020
+ <Heading as="h2">This is a default size Modal</Heading>
1021
+ )}
484
1022
  </Modal.Header>
485
1023
  <Modal.Body>
486
- <View as="p" margin="none none small"><Text>{fpo}</Text></View>
1024
+ <View as="p" margin="none none small">
1025
+ <Text>{fpo}</Text>
1026
+ </View>
487
1027
  </Modal.Body>
488
1028
  <Modal.Footer>
489
- <Button onClick={this.handleButtonClick} margin="0 x-small 0 0">Close</Button>
490
- <Button onClick={this.handleButtonClick} color="primary" type="submit">Submit</Button>
1029
+ <Button onClick={toggleOpen} margin="0 x-small 0 0">
1030
+ Close
1031
+ </Button>
1032
+ <Button onClick={toggleOpen} color="primary" type="submit">
1033
+ Submit
1034
+ </Button>
491
1035
  </Modal.Footer>
492
1036
  </Modal>
493
1037
 
@@ -495,19 +1039,17 @@ class Example extends React.Component {
495
1039
  background="primary-inverse"
496
1040
  margin="medium auto none"
497
1041
  display="block"
498
- width={this.state.smallViewport ? '20rem' : '50rem'}
1042
+ width={smallViewport ? '20rem' : '50rem'}
499
1043
  height="37.5rem"
500
1044
  borderWidth="large"
501
1045
  id="viewportExample"
502
- >
503
- </View>
1046
+ ></View>
504
1047
  </div>
505
1048
  )
506
1049
  }
507
- }
508
1050
 
509
- render(<Example />)
510
- ```
1051
+ render(<Example />)
1052
+ ```
511
1053
 
512
1054
  ### Guidelines
513
1055